package cn.tedu.charging.order.service.impl;

import cn.tedu.charging.common.pojo.message.CheckResultMessage;
import cn.tedu.charging.common.pojo.message.DelayCheckMessage;
import cn.tedu.charging.common.pojo.message.ProgressData;
import cn.tedu.charging.common.pojo.message.ProgressMessage;
import cn.tedu.charging.common.pojo.param.ProgressCostParam;
import cn.tedu.charging.common.pojo.vo.ProgressCostVO;
import cn.tedu.charging.common.protocol.JsonResult;
import cn.tedu.charging.common.protocol.WebSocketResult;
import cn.tedu.charging.common.utils.SnowflakeIdGenerator;
import cn.tedu.charging.common.utils.TimeConverterUtil;
import cn.tedu.charging.order.client.CostClient;
import cn.tedu.charging.order.dao.repository.BillRepository;
import cn.tedu.charging.order.dao.repository.ProcessEsRepository;
import cn.tedu.charging.order.pojo.po.ChargingBillFailPO;
import cn.tedu.charging.order.pojo.po.ChargingBillSuccessPO;
import cn.tedu.charging.order.pojo.po.ChargingProgressEsPO;
import cn.tedu.charging.order.service.ConsumerService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.tedu.charging.order.points.WebsocketServerPoint;
import org.springframework.web.socket.WebSocketMessage;

import java.util.Date;
import java.util.PrimitiveIterator;

/**
 * 消费的消息处理业务逻辑
 */
@Service
@Slf4j
public class ConsumerServiceImpl implements ConsumerService {
    @Autowired
    private BillRepository billRepository;
    @Autowired
    private WebsocketServerPoint websocketServerPoint;
    @Autowired
    private CostClient costClient;
    @Autowired
    private SnowflakeIdGenerator generator;
    @Autowired
    private ProcessEsRepository processEsRepository;
    //在设备自检反馈信息之后,业务处理的实现方法
    @Override
    public void handlerCheckResult(CheckResultMessage msg) {
        log.debug("处理设备自检消费业务,消息数据:{}",msg);
        //添加幂等处理方案 每次执行消费,先检查订单是否已经存在了,如果已存在说明消费消息重复执行的
        Boolean exists = billRepository.existsBill(msg.getOrderNo());
        if (exists){
            log.info("消息重复消费,无需执行");
            return;
        }
        //1.从消息拿到result
        Boolean result = msg.getResult();
        //1.1即将根据自检反馈结果,封装WebSocketMessage
        WebSocketResult<String> wsMessage=new WebSocketResult<>();
        if (result){
            log.debug("设备自检成功");
            //2.组织封装success 写入数据库
            saveBillSuccess(msg);
            //3.组织通知用户的消息,告诉用户订单启动成功 马上充电 state目前没有单独在前端设计分支,使用异常的状态通知成功
            wsMessage.setState(1);
            wsMessage.setMessage("订单启动成功");
            //data中的字符串才是给用户展示的
            wsMessage.setData("您的订单启动成功,马上开始充电,请注意");
        }else{
            log.debug("设备自检失败");
            //4.组织fail 写入数据库
            saveBillFail(msg);
            //5.调用设备服务修改设备的状态 TODO
            // 6.组织失败消息推送给用户 走人sorry
            wsMessage.setState(1);
            wsMessage.setMessage("订单启动失败");
            //data中的字符串才是给用户展示的
            //这里如果真的失败了,可以对接(openFeign远程调用)活动优惠券模块,获取一个赠送的优惠券连接地址
            String couponUrl=getCouponUrl();
            wsMessage.setData("您的订单启动失败,非常抱歉,请换枪充电,送您一张优惠券"+couponUrl);
        }
        //7.将不同分支的消息推送给用户端
        websocketServerPoint.pushMsg(msg.getUserId(), String.valueOf(wsMessage));
    }

    @Override
    public void handleChargingProgress(ProgressMessage msg) {
        //打印一个业务日志,消费业务收到消息
        log.debug("处理充电进度消费业务,消息数据:{}",msg);
        //1.检查消息参数 温度电压是否正常[轻做]
        Boolean deviceOK=checkDeviceTemperatorAndVoltage(msg);
        //2.计算价格 按照和计价中心的接口功能设计对接的业务 获取计价结果
        ProgressCostVO costVO=calculateCost(msg);
        //3.查询用户剩余余额[轻做]
        Double balance=getUserBalance(msg.getUserId());
        //4.将充电进度信息 组织封装写入es
        persistProgress(msg,costVO);
        //5.组织通知数据WebSocketMessage<String> WebSocketMessage<ProgressData>
        WebSocketResult wsMessage=new WebSocketResult<>();
        if (!deviceOK){
            //温度不正常
            wsMessage.setState(1);//异常和失败的状态
            wsMessage.setMessage("温度不正常");
            wsMessage.setData("对不起,该设备在充电过程中有问题,送您一张优惠券"+getCouponUrl());
        }else if (balance<costVO.getTotalCost()){
            //余额不正常
            wsMessage.setState(1);//异常和失败的状态
            wsMessage.setMessage("余额不足");
            wsMessage.setData("对不起,您的余额不足,请结束充电充值");
        }else{
            //一定都是正常在充电,正常接到了本次设备同步的信息
            //5.1progressData中准备了同步信息页面的所有参数
            ProgressData data=new ProgressData();
            data.setTotalCost(costVO.getTotalCost());
            data.setOneElectricityCost(costVO.getPowerFee());
            data.setChargingCapacity(costVO.getChargingCapacity());
            data.setTotalCapacity(msg.getTotalCapacity());
            //通过totalTime计算小时数 分钟数 描述
            data.setHours(TimeConverterUtil.getHour(msg.getTotalTime()).intValue());
            data.setMinutes(TimeConverterUtil.getMinute(msg.getTotalTime()).intValue());
            data.setSeconds(TimeConverterUtil.getSecond(msg.getTotalTime()).intValue());
            //wsMessage正常充电桩状态是3
            wsMessage.setState(3);
            wsMessage.setData(data);
            wsMessage.setMessage("正在充电");
        }
        //推送消息
        websocketServerPoint.pushMsg(msg.getUserId(), String.valueOf(wsMessage));
    }

    private ProgressCostVO calculateCost(ProgressMessage msg) {
        //1.准备接口入参ProgressCostParam
        ProgressCostParam param=new ProgressCostParam();
        BeanUtils.copyProperties(msg,param);
        //2.调用接口 访问计价
        JsonResult<ProgressCostVO> result = costClient.calculateCost(param);
        if (result.getCode()!=0){
            //调用失败
            log.error("调用计价接口失败,错误码:{},错误信息:{}",result.getCode(),result.getMessage());
            //返回空值,让失败的逻辑 由调用者处理,抛一个异常,调用者处理异常
            throw new RuntimeException("调用计价接口失败");
        }else{
            //3.解析vo返回给业务流程
            return result.getData();
        }
    }

    private void persistProgress(ProgressMessage msg, ProgressCostVO costVO) {
        //TODO持久化到es
        //1.组织一个po
        //1.1消息数据全部复制
        ChargingProgressEsPO po=new ChargingProgressEsPO();
        BeanUtils.copyProperties(msg,po);
        //1.2.costVO补充计算属性
        po.setChargingCapacity(costVO.getChargingCapacity());
        po.setTotalCost(costVO.getTotalCost());
        //1.3.补充一个文档唯一且时序上升的id值
        po.setId(generator.nextId()+"");
        //2.写入es
        processEsRepository.save(po);
    }

    private Double getUserBalance(Integer userId) {
        //TODO 远程调用一下user服务 拿到用户具体余额数字
        return 9999999.99;
    }

    private Boolean checkDeviceTemperatorAndVoltage(ProgressMessage msg) {
        //TODO 根据专业的领域计算,得到温度和电压的危险 警告信息
        return true;
    }

    private String getCouponUrl() {
        //利用优惠券couponClient调用,获取一个赠送优惠券连接
        String couponUrl="http://C8F32JFKF329";
        return couponUrl;
    }

    private void saveBillFail(CheckResultMessage msg) {
        //1.组织ChargingBillFailPO 对应表 charging_bill_fail
        ChargingBillFailPO fail=new ChargingBillFailPO();
        //1.1从消息直接获取的属性
        fail.setBillId(msg.getOrderNo());
        fail.setFailDesc(msg.getFailDesc());
        fail.setUserId(msg.getUserId());
        fail.setGunId(msg.getGunId());
        //TODO 1.2冗余属性可以使用消息数据单独查询封装 operator_id station_id vehicle_id
        //1.3业务生成的默认值
        fail.setCreateTime(new Date());
        fail.setUpdateTime(fail.getCreateTime());
        fail.setDeleted(0);//逻辑删除字段
        //2.数据仓储层入口 写入失败
        billRepository.saveFailBill(fail);
    }

    private void saveBillSuccess(CheckResultMessage msg) {
        //1.组织ChargingBillSuccessPO 对应表 charging_bill_success
        ChargingBillSuccessPO success=new ChargingBillSuccessPO();
        //1.1从消息补充消息字段
        success.setUserId(msg.getUserId());
        success.setBillId(msg.getOrderNo());
        success.setGunId(msg.getGunId());
        //1.2根本不是当前启动成功阶段该填写字段 默认 charging_capacity charging_duration charging_end_time..
        //1.3支付订单完成才补充字段 pay_amount pay_time..
        //1.4业务数据 bill_status启动成功 正在充电 charging_start_time create_time update_time delete
        success.setBillStatus(1);
        success.setChargingStartTime(new Date());
        success.setCreateTime(success.getChargingStartTime());
        success.setUpdateTime(success.getChargingStartTime());
        success.setDeleted(0);
        //2.调用仓储层写入
        billRepository.saveSuccessBill(success);
    }

    @Override
    public void handleCheckNoRes(DelayCheckMessage msg) {
        //1.封装一个方法 使用订单编号 查询订单是否存在success或者fail
        Boolean exists=billRepository.existsBill(msg.getOrderNo());
        //判断不存在
        if (!exists){
            log.info("当前检查的订单不存在,所以按照无响应处理");
            //不存在成功 也不存在失败 无响应
            //2.按照失败处理
            saveBillFail(msg);
            //3.实时推送消息通知 别等了 已经超时了 sorry
            WebSocketResult<String> wsMessage=new WebSocketResult<>();
            wsMessage.setState(1);
            wsMessage.setMessage("订单启动失败");
            String couponUrl = getCouponUrl();
            wsMessage.setData("您的订单启动失败,非常抱歉,请换枪充电,送您一张优惠券"+couponUrl);
            websocketServerPoint.pushMsg(msg.getUserId(), String.valueOf(wsMessage));
            //4.远程调用设备服务 修改枪的状态 TODO
        }
    }
    private void saveBillFail(DelayCheckMessage msg) {
        //1.组织ChargingBillFailPO 对应表 charging_bill_fail
        ChargingBillFailPO fail=new ChargingBillFailPO();
        //1.1从消息直接获取的属性
        fail.setBillId(msg.getOrderNo());
        fail.setFailDesc("无响应失败");
        fail.setUserId(msg.getUserId());
        fail.setGunId(msg.getGunId());
        //TODO 1.2冗余属性可以使用消息数据单独查询封装 operator_id station_id vehicle_id
        //1.3业务生成的默认值
        fail.setCreateTime(new Date());
        fail.setUpdateTime(fail.getCreateTime());
        fail.setDeleted(0);//逻辑删除字段
        //2.数据仓储层入口 写入失败
        billRepository.saveFailBill(fail);
    }
}
